#include "..\\include\Lib_Clara.h"
#include "..\\include\Dir.h"
#include "structs.h"

#include "XML.c"                                                                //My small XML parser

#define _AUTHOR_INFO_ "ClearBass Equalizer v2.4\n(c)mc_kibel\n(r) juLiONaru"
#define _GC_PutChar(_imgID_,_x_,_y_) GC_PutChar(gc,_x_,_y_,0,0,_imgID_)
#define EQUALIZER_START_EVENT 0x3020

int sesid;                                                                      //Stores AudioSessionID
int pos = 1;                                                                    //Position of 'cursor' in presets
int manpos = 2;                                                                 //Position of 'curson' in manual settings
int preset_restored = 0;
int images_destroyed = 1;

MyBOOK * MyBook;
EQ_PRESET *eq;
u16 redr_timer;
GC *gc;
static const char MyGuiName[]="ClearBass_GUI";
wchar_t conf_path[256] = {0,};

wchar_t *imgPresets[]={
  L"Bg.png",    
  L"Img1.png",  L"Img2.png",    L"Img3.png",
  L"Img4.png",  L"Img5.png",    L"Img6.png",    
  L"Img7.png",  L"Img8.png",    L"Img9.png"
};
wchar_t imgPresetsID[]={
  0xFFFF,
  0xFFFF,0xFFFF,0xFFFF,
  0xFFFF,0xFFFF,0xFFFF,
  0xFFFF,0xFFFF,0xFFFF
};

wchar_t *imgLevels[]={
  L"Level1.png",  L"Level2.png",
  L"Level3.png",  L"Level4.png"
};
wchar_t imgLevelsID[]={
  0xFFFF,0xFFFF,
  0xFFFF,0xFFFF
};

wchar_t *imgManual[]={
  L"Minus3.png",  L"Minus2.png",  L"Minus1.png",
  L"Neutral.png",
  L"Plus1.png",   L"Plus2.png",   L"Plus3.png"
};
wchar_t imgManualID[]={
  0xFFFF,0xFFFF,0xFFFF,
  0xFFFF,
  0xFFFF,0xFFFF,0xFFFF
};

int redrect_positions[6][4]; //XML data will be loaded here
int bands_positions [6][2]; //XML data will be loaded here

int settings_table [][5] = {
  {   1,    -1,   -2,   3,    1   },  //Treble
  {   0,    1,    -5,   -4,   4   },  //Heavy
  {   1,    -6,   -5,   -3,   4   },  //Pop
  {   0,    0,    -5,   -3,   1   },  //Jazz
  {   0,    0,    0,    0,    0   },  //Normal
  {   2,    -6,   -5,   -3,   4   },  //Special
  {   2,    -2,   -3,   -1,   3   },  //Soul
  {   1,    -3,   -3,   -3,   1   },  //Light
  {   9,    -6,   -4,   -3,   9   }   //Bass
};

int OnAudioPlaying (void *mess, BOOK *bk);
int OnStart (void *mess, BOOK *bk);

void EQUALIZER_GetAudioSessionID(int *sid_ptr)
{
  int done;
  int pos = 10;
  done = EqualizerGain_Set( pos, 0, -8 );
  while (done != 0)
  {
    ++pos;
    done = EqualizerGain_Set( pos, 0, -8 );
  } 
  if (done == 0)
  {
    *sid_ptr = pos;
    debug_printf ("\n -->> ClearBass: AudioSessionID %d \n",pos);
  }
}

void EQUALIZER_GetEqualizerposition (int curpos, EQ_PRESET *p) 
{
  int i = curpos - 1;
  p->b1 = settings_table[i][0];
  p->b2 = settings_table[i][1];
  p->b3 = settings_table[i][2];
  p->b4 = settings_table[i][3];
  p->b5 = settings_table[i][4];
}

void EQUALIZER_SetEqualizer (EQ_PRESET *p) 
{
  EqualizerGain_Set (sesid,0,p->b1);
  EqualizerGain_Set (sesid,1,p->b2);
  EqualizerGain_Set (sesid,2,p->b3);
  EqualizerGain_Set (sesid,3,p->b4);
  EqualizerGain_Set (sesid,4,p->b5);
  debug_printf ("\n -->> ClearBass: Equalizer set to %d, %d, %d, %d, %d \n",p->b1,p->b2,p->b3,p->b4,p->b5);
}

void EQUALIZER_SaveEqd () 
{
  int file = _fopen (conf_path,L"LastSettings.eqd",WA_Write,0x180,0);
  fwrite(file,eq,sizeof(EQ_PRESET));
  fclose (file);
}

int EQUALIZER_LoadEqd ()
{
  FSTAT _fst;
  if (fstat(conf_path,L"LastSettings.eqd",&_fst)==0)
  {
    int file = _fopen (conf_path,L"LastSettings.eqd",WA_Read,0x180,0);
    memset (eq,0,sizeof(EQ_PRESET));
    fread(file,eq,sizeof(EQ_PRESET));
    fclose (file);
    return 1;
  }
  eq->b1 = 0;
  eq->b2 = 0;
  eq->b3 = 0;
  eq->b4 = 0;
  eq->b5 = 0;
  eq->equalizer = 1;
  eq->boost = 0;
  eq->b1lvl = 0;
  eq->b2lvl = 0;
  eq->b3lvl = 0;
  eq->b4lvl = 0;
  eq->b5lvl = 0;
  eq->pos = 1;
  return 0;
}

void EQUALIZER_LoadEqs (int preset_num)
{
  wchar_t name[32];
  memset (name,0,32);
  snwprintf(name,31,L"Preset%d.eqs",preset_num);
  EQS *eqs = (EQS*)malloc(sizeof(EQS));
  memset (eqs,0,sizeof(EQS));
  FSTAT _fst;
  if (fstat(conf_path,name,&_fst) == 0)
  {
    int f = _fopen (conf_path,name,WA_Read,0x180,0);
    fread (f,eqs,sizeof(EQS));
    fclose (f);
    settings_table[preset_num-1][0] = eqs->band1 - 20;
    settings_table[preset_num-1][1] = eqs->band2 - 20;
    settings_table[preset_num-1][2] = eqs->band3 - 20;
    settings_table[preset_num-1][3] = eqs->band4 - 20;
    settings_table[preset_num-1][4] = eqs->band5 - 20;
    debug_printf ("\n -->> ClearBass: Loaded external EQS file for preset %d: %d, %d, %d, %d, %d \n", 
                preset_num,
                settings_table[preset_num-1][0],
                settings_table[preset_num-1][1],
                settings_table[preset_num-1][2],
                settings_table[preset_num-1][3],
                settings_table[preset_num-1][4]);
  }
  mfree (eqs);
}

int ShowAuthorInfo(void *mess ,BOOK* book)
{
  MSG * msg = (MSG*)mess;
  MessageBox(0x6fFFFFFF,STR(_AUTHOR_INFO_),0, 1 ,5000,msg->book);
  return(1);
}

int TerminateElf(void * ,BOOK* book)
{
  FreeBook(book);
  return(1);
}

int OnMusicApplication_Create(void *mess, BOOK *bk)
{
  preset_restored = 0;
  return 0;
}

const PAGE_MSG BC_PageEvents[]@ "DYN_PAGE" ={
  EQUALIZER_START_EVENT,                          OnStart,
  UI_MEDIAPLAYER_AUDIO_PLAYING_TIME_EVENT_TAG,    OnAudioPlaying,
  UI_MEDIAPLAYER_CREATED_EVENT_TAG,               OnMusicApplication_Create,
  ELF_TERMINATE_EVENT ,                           TerminateElf,
  ELF_SHOW_INFO_EVENT  ,                          ShowAuthorInfo,
  NIL_EVENT_TAG,                                  NULL
};
PAGE_DESC bk_base ={"BC_BasePage",0,BC_PageEvents};


void elf_exit(void)
{
  kill_data(&ELF_BEGIN, (void(*)(void*))mfree_adr());
}

void InitImages(void)
{
  int i;
  for (i = 0 ; i < sizeof(imgPresetsID)/2 ; i++) ImageID_Get(conf_path,imgPresets[i],&imgPresetsID[i]);
  for (i = 0 ; i < sizeof(imgLevelsID)/2 ; i++) ImageID_Get(conf_path,imgLevels[i],&imgLevelsID[i]);
  for (i = 0 ; i < sizeof(imgManualID)/2 ; i++) ImageID_Get(conf_path,imgManual[i],&imgManualID[i]);
  images_destroyed = 0;
}

void DestroyImages(void)
{
  int i;
  for (i = 0 ; i < sizeof(imgPresetsID)/2 ; i++) ImageID_Free(imgPresetsID[i]);
  for (i = 0 ; i < sizeof(imgLevelsID)/2 ; i++) ImageID_Free(imgLevelsID[i]);
  for (i = 0 ; i < sizeof(imgManualID)/2 ; i++) ImageID_Free(imgManualID[i]);
  images_destroyed = 1;
}

void onMyBookClose(BOOK * book)
{
  mfree(eq);
  if (images_destroyed == 0) DestroyImages();
  SUBPROC(elf_exit);
}

void onRedrTimer (u16 unk , void * data)
{
  DispObject_InvalidateRect(GUIObject_GetDispObject(MyBook->gui), 0); 
  Timer_ReSet(&redr_timer,30,onRedrTimer,0);
}

int MyGuiOnCreate(DISP_OBJ *db)
{
  gc = get_DisplayGC();
  redr_timer=Timer_Set(1,onRedrTimer,0);
  return 1;
}

void MyGuiOnKey(DISP_OBJ *db,int key,int unk,int repeat,int type)
{
  if (type==KBD_SHORT_RELEASE) 
  {
    if (eq->equalizer==1)
    {
      if (key==KEY_RIGHT)
      {
        if (eq->pos<9) eq->pos++;
        else eq->pos = 1;
        EQUALIZER_GetEqualizerposition(eq->pos,eq);
        EQUALIZER_SetEqualizer(eq);
      } 
      else if (key==KEY_LEFT)
      {
        if (eq->pos>1) eq->pos--;
        else eq->pos = 9;
        EQUALIZER_GetEqualizerposition(eq->pos,eq);
        EQUALIZER_SetEqualizer(eq);
      } 
    } 
    else if (eq->equalizer==2)
    {
      if (key==KEY_RIGHT)
      {
        if (manpos<6) manpos++;
        else manpos = 1;
      } 
      else if (key==KEY_LEFT)
      {
        if (manpos>1) manpos--;
        else manpos = 6;
      } 
      if (manpos==1)
      {
        if (key==KEY_UP && eq->boost<3)
        {
          eq->boost++;
          eq->b1 = eq->b1lvl * (eq->boost+1);
          eq->b5 = eq->b5lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,0,eq->b1);
          EqualizerGain_Set(sesid,4,eq->b5);
        }
        else if (key==KEY_DOWN && eq->boost>0)
        {
          eq->boost--;
          eq->b1 = eq->b1lvl * (eq->boost+1);
          eq->b5 = eq->b5lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,0,eq->b1);
          EqualizerGain_Set(sesid,4,eq->b5);
        }
      } 
      else if (manpos==2)
      {
        if (key==KEY_UP && eq->b1lvl<3)
        {
          eq->b1lvl++;
          eq->b1 = eq->b1lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,0,eq->b1);
        }
        else if (key==KEY_DOWN && eq->b1lvl>(-3))
        {
          eq->b1lvl--;
          eq->b1 = eq->b1lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,0,eq->b1);
        }
      } 
      else if (manpos==3)
      {
        if (key==KEY_UP && eq->b2lvl<3)
        {
          eq->b2lvl++;
          eq->b2 = eq->b2lvl;
          EqualizerGain_Set(sesid,1,eq->b2);
        }
        else if (key==KEY_DOWN && eq->b2lvl>(-3))
        {
          eq->b2lvl--;
          eq->b2 = eq->b2lvl;
          EqualizerGain_Set(sesid,1,eq->b2);
        }
      } 
      else if (manpos==4)
      {
        if (key==KEY_UP && eq->b3lvl<3)
        {
          eq->b3lvl++;
          eq->b3 = eq->b3lvl;
          EqualizerGain_Set(sesid,2,eq->b3);
        }
        else if (key==KEY_DOWN && eq->b3lvl>(-3))
        {
          eq->b3lvl--;
          eq->b3 = eq->b3lvl;
          EqualizerGain_Set(sesid,2,eq->b3);
        }
      } 
      else if (manpos==5)
      {
        if (key==KEY_UP && eq->b4lvl<3)
        {
          eq->b4lvl++;
          eq->b4 = eq->b4lvl;
          EqualizerGain_Set(sesid,3,eq->b4);
        }
        else if (key==KEY_DOWN && eq->b4lvl>(-3))
        {
          eq->b4lvl--;
          eq->b4 = eq->b4lvl;
          EqualizerGain_Set(sesid,3,eq->b4);
        }
      } 
      else if (manpos==6)
      {
        if (key==KEY_UP && eq->b5lvl<3)
        {
          eq->b5lvl++;
          eq->b5 = eq->b5lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,4,eq->b5);
        }
        else if (key==KEY_DOWN && eq->b5lvl>(-3))
        {
          eq->b5lvl--;
          eq->b5 = eq->b5lvl * (eq->boost+1);
          EqualizerGain_Set(sesid,4,eq->b5);
        }
      }
    }
    DispObject_InvalidateRect(GUIObject_GetDispObject(MyBook->gui), 0); 
  }
}

void DrawPresets ()
{
  _GC_PutChar(imgPresetsID[eq->pos],0,0);
}

void DrawManualEq()
{
  _GC_PutChar(imgPresetsID[0],0,0);
  _GC_PutChar(imgLevelsID[eq->boost],
              bands_positions[0][0],
              bands_positions[0][1]);
  _GC_PutChar(imgManualID[eq->b1lvl+3],
              bands_positions[1][0],
              bands_positions[1][1]);
  _GC_PutChar(imgManualID[eq->b2lvl+3],
              bands_positions[2][0],
              bands_positions[2][1]);
  _GC_PutChar(imgManualID[eq->b3lvl+3],
              bands_positions[3][0],
              bands_positions[3][1]);
  _GC_PutChar(imgManualID[eq->b4lvl+3],
              bands_positions[4][0],
              bands_positions[4][1]);
  _GC_PutChar(imgManualID[eq->b5lvl+3],
              bands_positions[5][0],
              bands_positions[5][1]);
  DrawRect (redrect_positions[manpos-1][0], 
            redrect_positions[manpos-1][1], 
            redrect_positions[manpos-1][0] + redrect_positions[manpos-1][2], 
            redrect_positions[manpos-1][1] + redrect_positions[manpos-1][3], 
            0x00000000, 0xFFF23F22 );
}

void MyGuiOnRedraw(DISP_OBJ *db,int ,int ,int )
{
  if (eq->equalizer == 1)
    DrawPresets();
  else
    DrawManualEq();  
}

void MyGui_destr(GUI *desc) {}
void MyGuiOnClose(DISP_OBJ *db) {}

void MyGui_constr(DISP_DESC *desc)
{
  DISP_DESC_SetName(desc,MyGuiName);
  DISP_DESC_SetSize(desc,sizeof(DISP_OBJ));
  DISP_DESC_SetOnCreate(desc,(DISP_OBJ_ONCREATE_METHOD)MyGuiOnCreate);
  DISP_DESC_SetOnClose(desc,(DISP_OBJ_ONCLOSE_METHOD)MyGuiOnClose);
  DISP_DESC_SetOnRedraw(desc,(DISP_OBJ_ONREDRAW_METHOD)MyGuiOnRedraw);
  DISP_DESC_SetOnKey(desc,(DISP_OBJ_ONKEY_METHOD)MyGuiOnKey);
}


GUI *CreateGUI(BOOK *bk)
{
  MyBook->gui=new GUI;
  if (!GUIObject_Create(MyBook->gui,MyGui_destr,MyGui_constr, bk,0,0,0))
  {
    delete MyBook->gui;
    return 0;    
  }
  if (bk) BookObj_AddGUIObject(bk,MyBook->gui);
  return MyBook->gui;
}

void SwitchEq (BOOK *bk, GUI *g)
{
  if (eq->equalizer == 1)
  {
    eq->equalizer = 2;
    eq->b1 = eq->b1lvl * (eq->boost+1);
    eq->b2 = eq->b2lvl;
    eq->b3 = eq->b3lvl;
    eq->b4 = eq->b4lvl;
    eq->b5 = eq->b5lvl * (eq->boost+1);
  }
  else
  {
    eq->equalizer = 1;
    EQUALIZER_GetEqualizerposition (eq->pos,eq);
  }
  EQUALIZER_SetEqualizer(eq);
}

void OnSelect (BOOK *bk, GUI *g)
{
  EQUALIZER_SaveEqd();
  BookObj_Hide(bk,0);
  GUIObject_Destroy(MyBook->gui);
  DestroyImages();
  Timer_Kill(&redr_timer);
}

int CreateEqualizerGui (void)
{
  InitImages();
  MyBook->gui = CreateGUI((BOOK*)MyBook);
  GUIObject_SetStyle(MyBook->gui, 4);
  GUIObject_SetTitleType(MyBook->gui, 1);
  GUIObject_SoftKeys_Hide(MyBook->gui);
  GUIObject_SoftKeys_SetAction (MyBook->gui,ACTION_SELECT, OnSelect);
  GUIObject_SoftKeys_SetAction (MyBook->gui,ACTION_MORE, SwitchEq);
  GUIObject_SoftKeys_SetAction (MyBook->gui,ACTION_LONG_BACK, OnSelect);
  GUIObject_SoftKeys_SetAction (MyBook->gui,ACTION_BACK, OnSelect);
  DispObject_SetLayerColor(GUIObject_GetDispObject(MyBook->gui),0);
  int ChipID = GetChipID();
  if (ChipID != 0x7100 && ChipID != 0x8000 && ChipID != 0x8040 && ChipID != 0x9900) GUIObject_SoftKeys_RemoveBackground(MyBook->gui);
  GUIObject_SoftKeys_Show(MyBook->gui);
  GUIObject_Show(MyBook->gui); 
  return 0;
}

int isClearBassBook(BOOK * book)
{
  if(!strcmp(book->xbook->name,"ClearBass")) return 1;
  return 0;
}

int OnAudioPlaying (void *mess, BOOK *bk)
{
  if (preset_restored == 0)
  {
    EQUALIZER_GetAudioSessionID (&sesid);
    if (EQUALIZER_LoadEqd() == 0)
    {
      EQUALIZER_GetEqualizerposition (eq->pos,eq);
    }
    else
    {
      if (eq->equalizer == 2)
      {
        eq->b1 = eq->b1lvl * (eq->boost+1);
        eq->b2 = eq->b2lvl;
        eq->b3 = eq->b3lvl;
        eq->b4 = eq->b4lvl;
        eq->b5 = eq->b5lvl * (eq->boost+1);
      }
      else
        EQUALIZER_GetEqualizerposition (eq->pos,eq);
    }      
    EQUALIZER_SetEqualizer(eq);
    preset_restored = 1;
  }
  return 0;
}

int OnStart (void *mess, BOOK *bk)
{
  if (Display_GetTopBook(0) == FindBook(isAudioPlayerBook()))
  {
    for (int i = 0 ; i < 10 ; i++) EQUALIZER_LoadEqs(i);
    EQUALIZER_GetAudioSessionID (&sesid);
    if (EQUALIZER_LoadEqd() == 0)
    {
      EQUALIZER_GetEqualizerposition (eq->pos,eq);
    }
    else
    {
      if (eq->equalizer == 2)
      {
        eq->b1 = eq->b1lvl * (eq->boost+1);
        eq->b2 = eq->b2lvl;
        eq->b3 = eq->b3lvl;
        eq->b4 = eq->b4lvl;
        eq->b5 = eq->b5lvl * (eq->boost+1);
      }
      else
        EQUALIZER_GetEqualizerposition (eq->pos,eq);
    }      
    EQUALIZER_SetEqualizer(eq);
    CreateEqualizerGui();
    BookObj_SetFocus(bk,0);
  }
  return 0;
}


int LoadLayout()
{
  FSTAT _fst; //Once again...
  if (fstat(conf_path,L"Equalizer_Display.xml",&_fst) == 0)
  {
    int f = _fopen (conf_path,L"Equalizer_Display.xml",WA_Read,0x180,0);
    int params_count = 2;
    char band_params[2][16] = {0,};
    strcat (band_params[0],"xpos");
    strcat (band_params[1],"ypos");
    char dest[2][10] = {0,};
    char tree1_name[32] = {0,};
    int k;
    for (k = 0 ; k < 6 ; k++)
    {
        sprintf (tree1_name,"Band%d",k);
        XML_FindParamsInTree(f,tree1_name,params_count,band_params,dest);
        strings2int(dest,bands_positions[k],params_count);
    }
    params_count = 4;
    char redrect_params[4][16] = {0,};
    strcat (redrect_params[0],"xpos");
    strcat (redrect_params[1],"ypos");
    strcat (redrect_params[2],"width");
    strcat (redrect_params[3],"height");
    char dest2[4][10] = {0,};
    for (k = 0 ; k < 6 ; k++)
    {
        sprintf (tree1_name,"RedRect_Band%d",k);
        XML_FindParamsInTree(f,tree1_name,params_count,redrect_params,dest2);
        strings2int(dest2,redrect_positions[k],params_count);
    }
    fclose (f);
    return 1;
  }
  return 0;
}
  
int main (void)
{
  if (!FindBook(isClearBassBook))
  {
    MyBook=new MyBOOK;
    memset(MyBook,0,sizeof(MyBOOK));
    if (!CreateBook((BOOK*)MyBook,onMyBookClose,&bk_base,"ClearBass",-1,0))  
    {
      delete MyBook;
      SUBPROC(elf_exit);
      return (0);
    }
    wstrcpy(conf_path,GetDir(DIR_ELFS_CONFIG | MEM_INTERNAL));
    wstrcat(conf_path,L"/Equalizer/");
    eq = (EQ_PRESET*)malloc(sizeof(EQ_PRESET));
    memset (eq,0,sizeof(EQ_PRESET));
    for (int i = 0 ; i < 10 ; i++) EQUALIZER_LoadEqs(i);
    if (LoadLayout() == 0)
    {
      MessageBox (0x6Fffffff,STR("Equalizer_Display.xml not found!"),0,1,0,0);
      FreeBook(FindBook(isClearBassBook));
      return 0;
    }
  }
  else MessageBox (0x6Fffffff,STR("ClearBass is already working!"),0,1,2000,0);
  return(0);
}
